package com.bfly.core.interceptor;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.bfly.common.reflect.ReflectUtils;
import com.bfly.common.util.DataConvertUtils;
import com.bfly.common.util.JwtUtil;
import com.bfly.core.cache.AdminMenuCacheUtil;
import com.bfly.core.cache.MenuCacheUtil;
import com.bfly.core.context.*;
import com.bfly.core.context.event.AdminMenuReloadEvent;
import com.bfly.core.enums.SysError;
import com.bfly.core.exception.ServiceResponseException;
import com.bfly.core.exception.UnLoginException;
import com.bfly.core.security.Login;
import com.bfly.core.security.PermissionCheck;
import com.bfly.mall.admin.entity.Admin;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * 管理员后台拦截器判断用户权限信息
 *
 * @author 长沙商脉科技有限公司
 * @date 2018/11/28 11:58
 */
@Component
public class ManageInterceptor extends HandlerInterceptorAdapter {

    private Logger logger = LoggerFactory.getLogger(ManageInterceptor.class);
    @Autowired
    private ApplicationContext context;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();

            IpThreadLocal.set(ContextUtil.getClientIp(request));
            ServletRequestThreadLocal.set(request);

            if (!needLogin(method)) {
                // 不需要登录直接返回true
                return true;
            }
            String token = request.getHeader("token");
            // 校验token
            if (StringUtils.isBlank(token) || !JwtUtil.validateToken(token)) {
                throw new UnLoginException();
            }

            DecodedJWT jwtData = JwtUtil.getJwtData(token);
            String userName = jwtData.getSubject();

            if(!AdminMenuCacheUtil.isExist(userName)){
                // 当服务器重启会丢失登录管理员菜单缓存,重新加载管理员缓存
                synchronized (ManageInterceptor.class) {
                    if(!AdminMenuCacheUtil.isExist(userName)) {
                        context.publishEvent(new AdminMenuReloadEvent(context, userName));
                    }
                }
            }

            if(needPermissionCheck(method)) {
                if (!hasRight(userName, request)) {
                    throw new ServiceResponseException(SysError.RESOURCE_NOT_OWNER, "没有权限访问!");
                }
            }

            int userId = DataConvertUtils.convertToInteger(jwtData.getIssuer());
            boolean reNew = JwtUtil.needReNewToken(token);
            if (reNew) {
                // 重新生成token 返回
                String newToken = JwtUtil.generateTokenForAdmin(userId, userName);
                response.addHeader("Access-Control-Expose-Headers", "token");
                response.addHeader("token", newToken);
            }

            Admin admin = new Admin();
            admin.setUserName(userName);
            admin.setId(userId);
            AdminThreadLocal.set(admin);
        }
        return true;
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        IpThreadLocal.clear();
        ServletRequestThreadLocal.clear();
        AdminThreadLocal.clear();
        PagerThreadLocal.clear();
        CacheableThreadLocal.clear();
    }

    /**
     * 是否需要登录
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/28 15:09
     */
    private boolean needLogin(Method mth) {
        Login login = ReflectUtils.getMethodLogin(mth);
        return login == null ? true : login.required();
    }

    /**
     * 是否需要做权限验证
     * @author 长沙商脉科技有限公司
     * @date 2020/12/28 19:05
     */
    private boolean needPermissionCheck(Method mth) {
        PermissionCheck check = ReflectUtils.getMethodPermissionCheck(mth);
        return check == null ? true : check.required();
    }

    /**
     * 判断是否具有访问权限
     *
     * @author 长沙商脉科技有限公司
     * @date 2019/7/13 18:18
     */
    private boolean hasRight(String userName, HttpServletRequest request) {
        boolean needVerify;  //判断请求地址是否需要做权限判断
        // 如果请求的地址在菜单配置权限中则需要做权限判断
        String url = String.valueOf(request.getAttribute("org.springframework.web.servlet.HandlerMapping.pathWithinHandlerMapping"));
        needVerify = MenuCacheUtil.isExistUrl(url);
        if (!needVerify) {
            // 真实请求的URL地址不存在则判断模糊地址匹配
            String matchUrl = String.valueOf(request.getAttribute("org.springframework.web.servlet.HandlerMapping.bestMatchingPattern"));
            needVerify = MenuCacheUtil.isExistUrl(matchUrl);
            if (!needVerify) {
                throw new ServiceResponseException(SysError.NOT_FOUND, url + "资源未授权!");
            }
            url = matchUrl;
        }
       boolean has = AdminMenuCacheUtil.isExistUrl(userName, url);
        if (!has) {
            logger.warn(String.format("请求地址 %s 没有授权", url));
        }
        return has;
    }
}